
<!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />

    <title>Writing Plugins &#8212; Leo 6.7.2 documentation</title>
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
    <link rel="stylesheet" type="text/css" href="_static/classic.css" />
    <link rel="stylesheet" type="text/css" href="_static/custom.css" />
    
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
    <script src="_static/jquery.js"></script>
    <script src="_static/underscore.js"></script>
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
    <script src="_static/doctools.js"></script>
    <script src="_static/sphinx_highlight.js"></script>
    
    <script src="_static/sidebar.js"></script>
    
    <link rel="index" title="Index" href="genindex.html" />
    <link rel="search" title="Search" href="search.html" />
    <link rel="next" title="Debugging with Leo" href="debuggers.html" />
    <link rel="prev" title="Controlling Syntax Coloring" href="coloring.html" /> 
  </head><body>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="debuggers.html" title="Debugging with Leo"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="coloring.html" title="Controlling Syntax Coloring"
             accesskey="P">previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" accesskey="U">Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">Writing Plugins</a></li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body" role="main">
            
  <section id="writing-plugins">
<h1>Writing Plugins<a class="headerlink" href="#writing-plugins" title="Permalink to this heading">¶</a></h1>
<p>Plugins modify how Leo works. With plugins you can give Leo new commands,
modify how existing commands work, or change any other aspect of Leo’s look
and feel.</p>
<p>leoPyRef.leo contains all of Leo’s official plugins. Studying this file is
a good way to learn how to write plugins.</p>
<p>Writing plugins is like writing any other Leo script.  See
<a class="reference external" href="tutorial-scripting.html">Scripting Leo with Python</a>. In particular:</p>
<ol class="arabic simple">
<li><p>Plugins can use any of Leo’s source code simply by importing any module
defined in leoPy.leo.</p></li>
<li><p>Plugins can register event handlers just like any other Leo script. For full
details, see the section called <a class="reference internal" href="#handling-events">Handling Events</a> later in this chapter.</p></li>
</ol>
<p>The rest of this chapters discusses topics related specifically to plugins.</p>
<div class="contents local topic" id="contents">
<p class="topic-title">Contents</p>
<ul class="simple">
<li><p><a class="reference internal" href="#id1" id="id2">Writing Plugins</a></p></li>
<li><p><a class="reference internal" href="#important-security-warnings" id="id3">Important security warnings</a></p></li>
<li><p><a class="reference internal" href="#documenting-plugins" id="id4">Documenting plugins</a></p></li>
<li><p><a class="reference internal" href="#c-ivars-properties" id="id5">c ivars &amp; properties</a></p></li>
<li><p><a class="reference internal" href="#handling-events" id="id6">Handling events</a></p>
<ul>
<li><p><a class="reference internal" href="#summary-of-event-handlers" id="id7">Summary of event handlers</a></p></li>
</ul>
</li>
<li><p><a class="reference internal" href="#support-for-unit-testing" id="id8">Support for unit testing</a></p></li>
</ul>
</div>
<section id="id1">
<h2><a class="toc-backref" href="#id2">Writing Plugins</a><a class="headerlink" href="#id1" title="Permalink to this heading">¶</a></h2>
<p>A plugin is a Python file in Leo’s plugins folder.</p>
<p>Every plugin should have a top-level init function that returns True if the plugin has been initialized properly. The init function typically:</p>
<ol class="arabic simple">
<li><p>Registers an onCreate event handler, called when Leo creates a new window.</p></li>
<li><p>Calls g.plugin_signon(__name__)</p></li>
</ol>
<p>For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">init</span><span class="p">():</span>
    <span class="k">if</span> <span class="o">&lt;&lt;</span> <span class="nb">all</span> <span class="n">imports</span> <span class="n">successful</span> <span class="o">&gt;&gt;</span><span class="p">:</span>
        <span class="n">g</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">(</span><span class="s1">&#39;after-create-leo-frame&#39;</span><span class="p">,</span><span class="n">onCreate</span><span class="p">)</span>
        <span class="n">g</span><span class="o">.</span><span class="n">plugin_signon</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">True</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="k">return</span> <span class="kc">False</span>
</pre></div>
</div>
<p>Plugins do <em>not</em> have automatic access to c, g and p.</p>
<p>Plugins define g by importing it:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">leo.core.leoGlobals</span> <span class="k">as</span> <span class="nn">g</span>
</pre></div>
</div>
<p>Plugins gain access to c using event handlers:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">controllers</span> <span class="o">=</span> <span class="p">{}</span>

<span class="k">def</span> <span class="nf">init</span><span class="p">():</span>
    <span class="n">g</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">(</span><span class="s1">&#39;after-create-leo-frame&#39;</span><span class="p">,</span><span class="n">onCreate</span><span class="p">)</span>
    <span class="k">return</span> <span class="kc">True</span>

<span class="k">def</span> <span class="nf">onCreate</span> <span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">keys</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">controllers</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;c&#39;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">c</span><span class="p">:</span>
        <span class="nb">hash</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="n">hash</span><span class="p">()</span>
        <span class="k">if</span> <span class="nb">hash</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">controllers</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
            <span class="n">controllers</span><span class="p">(</span><span class="nb">hash</span><span class="p">)</span> <span class="o">=</span> <span class="n">PluginController</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">eventHander</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span><span class="n">keys</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">controllers</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;c&#39;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">c</span><span class="p">:</span>
        <span class="n">controller</span> <span class="o">=</span> <span class="n">controllers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">hash</span><span class="p">())</span>
        <span class="n">controller</span><span class="o">.</span><span class="n">handleEvent</span><span class="p">()</span>
</pre></div>
</div>
<p>Some plugins inject ivars into the Commands class rather than using a global controllers dict:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">onCreate</span> <span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">keys</span><span class="p">):</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;c&#39;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">c</span><span class="p">:</span>
        <span class="n">c</span><span class="o">.</span><span class="n">my_plugin_controller</span> <span class="o">=</span> <span class="n">ControllerClass</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">eventHander</span><span class="p">(</span><span class="n">tag</span><span class="p">,</span><span class="n">keys</span><span class="p">):</span>
    <span class="n">c</span> <span class="o">=</span> <span class="n">keys</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;c&#39;</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">c</span><span class="p">:</span>
        <span class="n">c</span><span class="o">.</span><span class="n">my_plugin_controller</span><span class="o">.</span><span class="n">handleEvent</span><span class="p">()</span>
</pre></div>
</div>
<p>Once c is determined, the presently selected position is simply c.p.</p>
</section>
<section id="important-security-warnings">
<h2><a class="toc-backref" href="#id3">Important security warnings</a><a class="headerlink" href="#important-security-warnings" title="Permalink to this heading">¶</a></h2>
<p>Naively using plugins can expose you and your .leo files to malicious attacks. The fundamental principles are:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Scripts</span> <span class="ow">and</span> <span class="n">plugins</span> <span class="n">must</span> <span class="n">never</span> <span class="n">blindly</span> <span class="n">execute</span> <span class="n">code</span> <span class="kn">from</span> <span class="nn">untrusted</span> <span class="n">sources</span><span class="o">.</span>
</pre></div>
</div>
<p>and:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">.</span><span class="n">leo</span> <span class="n">files</span> <span class="n">obtained</span> <span class="kn">from</span> <span class="nn">other</span> <span class="n">people</span> <span class="n">may</span> <span class="n">potentially</span> <span class="n">contain</span> <span class="n">hostile</span> <span class="n">code</span><span class="o">.</span>
</pre></div>
</div>
<p>Stephen Schaefer summarizes the danger this way:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>I foresee a future in which the majority of leo projects come from
marginally trusted sources...a world of leo documents sent hither
and yon - resumes, project proposals, textbooks, magazines,
contracts - and as a race of Pandora&#39;s, we cannot resist wanting
to see &quot;What&#39;s in the box?&quot; And are we going to fire up a text
editor to make a detailed examination of the ASCII XML? Never!
We&#39;re going to double click on the cute leo file icon, and leo
will fire up in all its raging glory. Just like Word (and its
macros) or Excel (and its macros).
</pre></div>
</div>
<p>In other words:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>When we share &quot;our&quot; .leo files we can NOT assume that we know what
is in our &quot;own&quot; documents!
</pre></div>
</div>
<p>Not all environments are untrustworthy. Code in a commercial cvs repository is probably trustworthy: employees might be terminated for posting malicious code. Still, the potential for abuse exists anywhere.</p>
<p>In Python it is very easy to write a script that will blindly execute other scripts:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Warning: extremely dangerous code</span>

<span class="c1"># Execute the body text of all nodes that start with `@script`.</span>
<span class="k">def</span> <span class="nf">onLoadFile</span><span class="p">():</span>
    <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">c</span><span class="o">.</span><span class="n">all_positions</span><span class="p">():</span>
        <span class="n">h</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">h</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">g</span><span class="o">.</span><span class="n">match_word</span><span class="p">(</span><span class="n">h</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="s2">&quot;@script&quot;</span><span class="p">):</span>
            <span class="n">s</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">b</span>
            <span class="k">if</span> <span class="n">s</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
                <span class="k">try</span><span class="p">:</span> <span class="c1"># SECURITY BREACH: s may be malicious!</span>
                    <span class="n">exec</span><span class="p">(</span><span class="n">s</span> <span class="o">+</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span>
                <span class="k">except</span><span class="p">:</span>
                    <span class="n">es_exception</span><span class="p">()</span>
</pre></div>
</div>
<p>Executing this kind of code is typically an intolerable security risk. <strong>Important</strong>: rexec provides <em>no protection whatever</em>. Leo is a repository of source code, so any text operation is potentially malicious. For example, consider the following script, which is valid in rexec mode:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>badNode = c.p
for p in c.all_positions():
    &lt;&lt; change `rexec` to `exec` in p&#39;s body &gt;&gt;
&lt;&lt; delete badNode &gt;&gt;
&lt;&lt; clear the undo stack &gt;&gt;
</pre></div>
</div>
<p>This script will introduce a security hole the .leo file without doing anything prohibited by rexec, and without leaving any traces of the perpetrating script behind. The damage will become permanent <em>outside</em> this script when the user saves the .leo file.</p>
</section>
<section id="documenting-plugins">
<h2><a class="toc-backref" href="#id4">Documenting plugins</a><a class="headerlink" href="#documenting-plugins" title="Permalink to this heading">¶</a></h2>
<p>Documenting new plugins is important for users to be able understand and use the features they add. To that effect, there are a few documentation steps that should not be overlooked.</p>
<ul>
<li><p>Document the plugin thoroughly in the plugin’s docstring. This allows the documentation to be accessed from the Plugins menu.</p></li>
<li><p>Document any new commands with a proper docstring. This allows the minibuffer command <cite>help-for-command</cite> to provide help for the command.</p></li>
<li><p>In <cite>leo/doc/sphinx-docs/sphinxDocs.leo</cite>, to the node <cite>&#64;file leo.plugins.rst</cite>, add the following snippet (preferably in alphabetical order), with the name of the plugin modified to the name of your plugin (here <cite>ipython</cite>). This allows the API docs to be automatically updated:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>:mod:`ipython` Module
---------------------

.. automodule:: leo.plugins.ipython
    :members:
    :undoc-members:
    :show-inheritance:
</pre></div>
</div>
</li>
</ul>
</section>
<section id="c-ivars-properties">
<h2><a class="toc-backref" href="#id5">c ivars &amp; properties</a><a class="headerlink" href="#c-ivars-properties" title="Permalink to this heading">¶</a></h2>
<p>For any commander c:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 41%" />
<col style="width: 59%" />
</colgroup>
<tbody>
<tr class="row-odd"><td><p><strong>Property</strong></p></td>
<td><p><strong>Value</strong></p></td>
</tr>
<tr class="row-even"><td><p>c.p</p></td>
<td><p>the presently selected position</p></td>
</tr>
<tr class="row-odd"><td><p><strong>Ivar</strong></p></td>
<td><p><strong>Value</strong></p></td>
</tr>
<tr class="row-even"><td><p>c.frame</p></td>
<td><p>the leoFrame representing the main window.</p></td>
</tr>
<tr class="row-odd"><td><p>c.frame.body</p></td>
<td><p>the leoBody representing the body pane.</p></td>
</tr>
<tr class="row-even"><td><p>c.frame.body.wrapper</p></td>
<td><p>a leoQTextEditWidget.</p></td>
</tr>
<tr class="row-odd"><td><p>c.frame.body.wrapper.widget</p></td>
<td><p>a LeoQTextBrowser (a QTextBrowser)</p></td>
</tr>
<tr class="row-even"><td><p>c.frame.tree</p></td>
<td><p>a leoQtTree, representing the tree pane</p></td>
</tr>
<tr class="row-odd"><td><p>c.frame.tree.treeWidget</p></td>
<td><p>a LeoQTreeWidget (a QTreeWidget)</p></td>
</tr>
<tr class="row-even"><td><p>c.user_dict</p></td>
<td><p>a Python dictionary for use by scripts and
plugins. Does not persist when Leo exists.</p></td>
</tr>
</tbody>
</table>
</section>
<section id="handling-events">
<h2><a class="toc-backref" href="#id6">Handling events</a><a class="headerlink" href="#handling-events" title="Permalink to this heading">¶</a></h2>
<p>Plugins and other scripts can register event handlers (also known as hooks):</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">leoPlugins</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">(</span><span class="s2">&quot;after-create-leo-frame&quot;</span><span class="p">,</span><span class="n">onCreate</span><span class="p">)</span>
<span class="n">leoPlugins</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">(</span><span class="s2">&quot;idle&quot;</span><span class="p">,</span> <span class="n">on_idle</span><span class="p">)</span>
<span class="n">leoPlugins</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">((</span><span class="s2">&quot;start2&quot;</span><span class="p">,</span><span class="s2">&quot;open2&quot;</span><span class="p">,</span><span class="s2">&quot;command2&quot;</span><span class="p">),</span> <span class="n">create_open_with_menu</span><span class="p">)</span>
</pre></div>
</div>
<p>As shown above, a plugin may register one or more event handlers with a single call to leoPlugins.registerHandler. Once a hook is registered, Leo will call the registered function’ at the named <strong>hook time</strong>. For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">leoPlugins</span><span class="o">.</span><span class="n">registerHandler</span><span class="p">(</span><span class="s2">&quot;idle&quot;</span><span class="p">,</span> <span class="n">on_idle</span><span class="p">)</span>
</pre></div>
</div>
<p>causes Leo to call on_idle at “idle” time.</p>
<p>Event handlers must have the following signature:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">myHook</span> <span class="p">(</span><span class="n">tag</span><span class="p">,</span> <span class="n">keywords</span><span class="p">):</span>
    <span class="n">whatever</span>
</pre></div>
</div>
<ul class="simple">
<li><p>tag is the name of the hook (a string).</p></li>
<li><p>keywords is a Python dictionary containing additional information. The following section describes the contents of the keywords dictionary in detail.</p></li>
</ul>
<p><strong>Important</strong>: hooks should get the proper commander this way:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">c</span> <span class="o">=</span> <span class="n">keywords</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;c&#39;</span><span class="p">)</span>
</pre></div>
</div>
<section id="summary-of-event-handlers">
<h3><a class="toc-backref" href="#id7">Summary of event handlers</a><a class="headerlink" href="#summary-of-event-handlers" title="Permalink to this heading">¶</a></h3>
<p>The following table tells about each event handler: its name, when it is called,
and the additional arguments passed to the hook in the keywords dictionary.
For some kind of hooks, Leo will skip its own normal processing if the hook
returns anything <em>other</em> than None. The table indicates such hooks with ‘yes’ in
the ‘Stop?’ column.</p>
<p><strong>Important</strong>: Ever since Leo 4.2, the v, old_v and new_v keys in
the keyword dictionary contain <em>positions</em>, not vnodes. These keys are
deprecated. The new_c key is also deprecated. Plugins should use the c key instead.</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 25%" />
<col style="width: 7%" />
<col style="width: 30%" />
<col style="width: 37%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Event name (tag argument)</p></th>
<th class="head"><p>Stop?</p></th>
<th class="head"><p>When called</p></th>
<th class="head"><p>Keys in keywords dict</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>‘after-auto’</p></td>
<td></td>
<td><p>after each &#64;auto file loaded</p></td>
<td><p>c,p (note 13)</p></td>
</tr>
<tr class="row-odd"><td><p>‘after-create-leo-frame’</p></td>
<td></td>
<td><p>after creating any frame</p></td>
<td><p>c</p></td>
</tr>
<tr class="row-even"><td><p>‘after-redraw-outline’</p></td>
<td></td>
<td><p>end of tree.redraw</p></td>
<td><p>c (note 6)</p></td>
</tr>
<tr class="row-odd"><td><p>‘before-create-leo-frame’</p></td>
<td></td>
<td><p>before frame.finishCreate</p></td>
<td><p>c</p></td>
</tr>
<tr class="row-even"><td><p>‘bodyclick1’</p></td>
<td><p>yes</p></td>
<td><p>before normal click in body</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘bodyclick2’</p></td>
<td></td>
<td><p>after normal click in body</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘bodydclick1’</p></td>
<td><p>yes</p></td>
<td><p>before double click in body</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘bodydclick2’</p></td>
<td></td>
<td><p>after  double click in body</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘bodykey1’</p></td>
<td><p>yes</p></td>
<td><p>before body keystrokes</p></td>
<td><p>c,p,v,ch,oldSel,undoType</p></td>
</tr>
<tr class="row-odd"><td><p>‘bodykey2’</p></td>
<td></td>
<td><p>after  body keystrokes</p></td>
<td><p>c,p,v,ch,oldSel,undoType</p></td>
</tr>
<tr class="row-even"><td><p>‘bodyrclick1’</p></td>
<td><p>yes</p></td>
<td><p>before right click in body</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘bodyrclick2’</p></td>
<td></td>
<td><p>after  right click in body</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘boxclick1’</p></td>
<td><p>yes</p></td>
<td><p>before click in +- box</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘boxclick2’</p></td>
<td></td>
<td><p>after  click in +- box</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘clear-all-marks’</p></td>
<td></td>
<td><p>after clear-all-marks command</p></td>
<td><p>c,p,v</p></td>
</tr>
<tr class="row-odd"><td><p>‘clear-mark’</p></td>
<td></td>
<td><p>when mark is set</p></td>
<td><p>c,p,v</p></td>
</tr>
<tr class="row-even"><td><p>‘close-frame’</p></td>
<td></td>
<td><p>in app.closeLeoWindow</p></td>
<td><p>c</p></td>
</tr>
<tr class="row-odd"><td><p>‘color-optional-markup’</p></td>
<td><p>yes *</p></td>
<td><p>(note 7)</p></td>
<td><p>colorer,p,v,s,i,j,colortag (note 7)</p></td>
</tr>
<tr class="row-even"><td><p>‘command1’</p></td>
<td><p>yes</p></td>
<td><p>before each command</p></td>
<td><p>c,p,v,label (note 2)</p></td>
</tr>
<tr class="row-odd"><td><p>‘command2’</p></td>
<td></td>
<td><p>after  each command</p></td>
<td><p>c,p,v,label (note 2)</p></td>
</tr>
<tr class="row-even"><td><p>‘create-optional-menus’</p></td>
<td></td>
<td><p>(note 8)</p></td>
<td><p>c (note 8)</p></td>
</tr>
<tr class="row-odd"><td><p>‘create-popup-menu-items’</p></td>
<td></td>
<td><p>in tree.OnPopup</p></td>
<td><p>c,p,v,event (new)</p></td>
</tr>
<tr class="row-even"><td><p>‘draw-outline-box’</p></td>
<td><p>yes</p></td>
<td><p>when drawing +- box</p></td>
<td><p>tree,p,v,x,y</p></td>
</tr>
<tr class="row-odd"><td><p>‘draw-outline-icon’</p></td>
<td><p>yes</p></td>
<td><p>when drawing icon</p></td>
<td><p>tree,p,v,x,y</p></td>
</tr>
<tr class="row-even"><td><p>‘draw-outline-node’</p></td>
<td><p>yes</p></td>
<td><p>when drawing node</p></td>
<td><p>tree,p,v,x,y</p></td>
</tr>
<tr class="row-odd"><td><p>‘draw-outline-text-box’</p></td>
<td><p>yes</p></td>
<td><p>when drawing headline</p></td>
<td><p>tree,p,v,x,y</p></td>
</tr>
<tr class="row-even"><td><p>‘drag1’</p></td>
<td><p>yes</p></td>
<td><p>before start of drag</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘drag2’</p></td>
<td></td>
<td><p>after  start of drag</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘dragging1’</p></td>
<td><p>yes</p></td>
<td><p>before continuing to drag</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘dragging2’</p></td>
<td></td>
<td><p>after  continuing to drag</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘enable-popup-menu-items’</p></td>
<td></td>
<td><p>in tree.OnPopup</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘end1’</p></td>
<td></td>
<td><p>start of app.quit()</p></td>
<td><p>None</p></td>
</tr>
<tr class="row-even"><td><p>‘enddrag1’</p></td>
<td><p>yes</p></td>
<td><p>before end of drag</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘enddrag2’</p></td>
<td></td>
<td><p>after  end of drag</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘headclick1’</p></td>
<td><p>yes</p></td>
<td><p>before normal click in headline</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘headclick2’</p></td>
<td></td>
<td><p>after  normal click in headline</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘headrclick1’</p></td>
<td><p>yes</p></td>
<td><p>before right click in headline</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘headrclick2’</p></td>
<td></td>
<td><p>after  right click in headline</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘headkey1’</p></td>
<td><p>yes</p></td>
<td><p>before headline keystrokes</p></td>
<td><p>c,p,v,ch (note 12)</p></td>
</tr>
<tr class="row-odd"><td><p>‘headkey2’</p></td>
<td></td>
<td><p>after  headline keystrokes</p></td>
<td><p>c,p,v,ch (note 12)</p></td>
</tr>
<tr class="row-even"><td><p>‘hoist-changed’</p></td>
<td></td>
<td><p>whenever the hoist stack changes</p></td>
<td><p>c</p></td>
</tr>
<tr class="row-odd"><td><p>‘hypercclick1’</p></td>
<td><p>yes</p></td>
<td><p>before control click in hyperlink</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘hypercclick2’</p></td>
<td></td>
<td><p>after  control click in hyperlink</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘hyperenter1’</p></td>
<td><p>yes</p></td>
<td><p>before entering hyperlink</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘hyperenter2’</p></td>
<td></td>
<td><p>after  entering hyperlink</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘hyperleave1’</p></td>
<td><p>yes</p></td>
<td><p>before leaving  hyperlink</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘hyperleave2’</p></td>
<td></td>
<td><p>after  leaving  hyperlink</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-odd"><td><p>‘iconclick1’</p></td>
<td><p>yes</p></td>
<td><p>before single click in icon box</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-even"><td><p>‘iconclick2’</p></td>
<td></td>
<td><p>after  single click in icon box</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-odd"><td><p>‘iconrclick1’</p></td>
<td><p>yes</p></td>
<td><p>before right click in icon box</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-even"><td><p>‘iconrclick2’</p></td>
<td></td>
<td><p>after  right click in icon box</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-odd"><td><p>‘icondclick1’</p></td>
<td><p>yes</p></td>
<td><p>before double click in icon box</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-even"><td><p>‘icondclick2’</p></td>
<td></td>
<td><p>after  double click in icon box</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-odd"><td><p>‘idle’</p></td>
<td></td>
<td><p>periodically (at idle time)</p></td>
<td><p>c</p></td>
</tr>
<tr class="row-even"><td><p>‘init-color-markup’</p></td>
<td></td>
<td><p>(note 7)</p></td>
<td><p>colorer,p,v (note 7)</p></td>
</tr>
<tr class="row-odd"><td><p>‘menu1’</p></td>
<td><p>yes</p></td>
<td><p>before creating menus</p></td>
<td><p>c,p,v (note 3)</p></td>
</tr>
<tr class="row-even"><td><p>‘menu2’</p></td>
<td><p>yes</p></td>
<td><p>during creating menus</p></td>
<td><p>c,p,v (note 3)</p></td>
</tr>
<tr class="row-odd"><td><p>‘menu-update’</p></td>
<td><p>yes</p></td>
<td><p>before updating menus</p></td>
<td><p>c,p,v</p></td>
</tr>
<tr class="row-even"><td><p>‘new’</p></td>
<td></td>
<td><p>start of New command</p></td>
<td><p>c,old_c,new_c (note 9)</p></td>
</tr>
<tr class="row-odd"><td><p>‘open1’</p></td>
<td><p>yes</p></td>
<td><p>before opening any file</p></td>
<td><p>c,old_c,new_c,fileName (note 4)</p></td>
</tr>
<tr class="row-even"><td><p>‘open2’</p></td>
<td></td>
<td><p>after  opening any file</p></td>
<td><p>c,old_c,new_c,fileName (note 4)</p></td>
</tr>
<tr class="row-odd"><td><p>‘openwith1’</p></td>
<td><p>yes</p></td>
<td><p>before Open With command</p></td>
<td><p>c,p,v,d (note 14)</p></td>
</tr>
<tr class="row-even"><td><p>‘openwith2’</p></td>
<td></td>
<td><p>after  Open With command</p></td>
<td><p>c,p,v,(note 14)</p></td>
</tr>
<tr class="row-odd"><td><p>‘recentfiles1’</p></td>
<td><p>yes</p></td>
<td><p>before Recent Files command</p></td>
<td><p>c,p,v,fileName</p></td>
</tr>
<tr class="row-even"><td><p>‘recentfiles2’</p></td>
<td></td>
<td><p>after  Recent Files command</p></td>
<td><p>c,p,v,fileName</p></td>
</tr>
<tr class="row-odd"><td><p>‘redraw-entire-outline’</p></td>
<td><p>yes</p></td>
<td><p>start of tree.redraw</p></td>
<td><p>c (note 6)</p></td>
</tr>
<tr class="row-even"><td><p>‘save1’</p></td>
<td><p>yes</p></td>
<td><p>before any Save command</p></td>
<td><p>c,p,v,fileName</p></td>
</tr>
<tr class="row-odd"><td><p>‘save2’</p></td>
<td></td>
<td><p>after  any Save command</p></td>
<td><p>c,p,v,fileName</p></td>
</tr>
<tr class="row-even"><td><p>‘scan-directives’</p></td>
<td></td>
<td><p>in scanDirectives</p></td>
<td><p>c,p,v,s,old_dict,dict,pluginsList (note 10)</p></td>
</tr>
<tr class="row-odd"><td><p>‘select1’</p></td>
<td><p>yes</p></td>
<td><p>before selecting a position</p></td>
<td><p>c,new_p,old_p,new_v,old_v</p></td>
</tr>
<tr class="row-even"><td><p>‘select2’</p></td>
<td></td>
<td><p>after  selecting a position</p></td>
<td><p>c,new_p,old_p,new_v,old_v</p></td>
</tr>
<tr class="row-odd"><td><p>‘select3’</p></td>
<td></td>
<td><p>after  selecting a position</p></td>
<td><p>c,new_p,old_p,new_v,old_v</p></td>
</tr>
<tr class="row-even"><td><p>‘set-mark’</p></td>
<td></td>
<td><p>when a mark is set</p></td>
<td><p>c,p,v</p></td>
</tr>
<tr class="row-odd"><td><p>‘show-popup-menu’</p></td>
<td></td>
<td><p>in tree.OnPopup</p></td>
<td><p>c,p,v,event</p></td>
</tr>
<tr class="row-even"><td><p>‘start1’</p></td>
<td></td>
<td><p>after app.finishCreate()</p></td>
<td><p>None</p></td>
</tr>
<tr class="row-odd"><td><p>‘start2’</p></td>
<td></td>
<td><p>after opening first Leo window</p></td>
<td><p>c,p,v,fileName</p></td>
</tr>
<tr class="row-even"><td><p>‘unselect1’</p></td>
<td><p>yes</p></td>
<td><p>before unselecting a vnode</p></td>
<td><p>c,new_p,old_p,new_v,old_v</p></td>
</tr>
<tr class="row-odd"><td><p>‘unselect2’</p></td>
<td></td>
<td><p>after  unselecting a vnode</p></td>
<td><p>c,new_p,old_p,old_v,old_v</p></td>
</tr>
<tr class="row-even"><td><p>‘&#64;url1’</p></td>
<td><p>yes</p></td>
<td><p>before double-click &#64;url node</p></td>
<td><p>c,p,v,url (note 5)</p></td>
</tr>
<tr class="row-odd"><td><p>‘&#64;url2’</p></td>
<td></td>
<td><p>after  double-click &#64;url node</p></td>
<td><p>c,p,v(note 5)</p></td>
</tr>
</tbody>
</table>
<p><strong>Notes</strong>:</p>
<ol class="arabic">
<li><p>‘activate’ and ‘deactivate’ hooks have been removed because they do not work as expected.</p></li>
<li><p>‘commands’ hooks: The label entry in the keywords dict contains the
‘canonicalized’ form of the command, that is, the lowercase name of the command
with all non-alphabetic characters removed.
Commands hooks now set the label for undo and redo commands ‘undo’ and ‘redo’
rather than ‘cantundo’ and ‘cantredo’.</p></li>
<li><p>‘menu1’ hook: Setting g.app.realMenuNameDict in this hook is an easy way of
translating menu names to other languages. <strong>Note</strong>: the ‘new’ names created this
way affect only the actual spelling of the menu items, they do <em>not</em> affect how
you specify shortcuts settings, nor do they affect the ‘official’
command names passed in g.app.commandName. For example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">app</span><span class="p">()</span><span class="o">.</span><span class="n">realMenuNameDict</span><span class="p">[</span><span class="s1">&#39;Open...&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;Ouvre&#39;</span><span class="o">.</span>
</pre></div>
</div>
</li>
<li><p>‘open1’ and ‘open2’ hooks: These are called with a keywords dict containing the following entries:</p>
<ul class="simple">
<li><p>c:          The commander of the newly opened window.</p></li>
<li><p>old_c:      The commander of the previously open window.</p></li>
<li><p>new_c:      (deprecated: use ‘c’ instead) The commander of the newly opened window.</p></li>
<li><p>fileName:   The name of the file being opened.</p></li>
</ul>
<p>You can use old_c.p and c.p to get the current position in the old and new windows.
Leo calls the ‘open1’ and ‘open2’ hooks only if the file is not already open. Leo
will also call the ‘open1’ and ‘open2’ hooks if: a) a file is opened using the
Recent Files menu and b) the file is not already open.</p>
</li>
<li><p><a class="reference external" href="mailto:'&#37;&#52;&#48;url1">‘<span>&#64;</span>url1</a>’ and <a class="reference external" href="mailto:'&#37;&#52;&#48;url2">‘<span>&#64;</span>url2</a>’ hooks are only executed if the ‘icondclick1’ hook returns None.</p></li>
<li><p>These hooks are useful for testing.</p></li>
<li><p>These hooks allow plugins to parse and handle markup within doc parts,
comments and Python ‘’’ strings. Note that these hooks are <em>not</em> called in
Python ‘’’ strings. See the color_markup plugin for a complete example of how to
use these hooks.</p></li>
<li><p>Leo calls the ‘create-optional-menus’ hook when creating menus. This hook need
only create new menus in the correct order, without worrying about the placement
of the menus in the menu bar. See the plugins_menu and scripts_menu plugins for
examples of how to use this hook.</p></li>
<li><p>The New command calls ‘new’.
The ‘new_c’ key is deprecated.  Use the ‘c’ key instead.</p></li>
<li><p>g.scanDirectives calls ‘scan-directives’ hook.
g.scanDirectives returns a dictionary, say d.
d.get(‘pluginsList’) is an a list of tuples (d,v,s,k) where:</p>
<ul class="simple">
<li><p>d is the spelling of the &#64;directive, without the leading &#64;.</p></li>
<li><p>v is the vnode containing the directive, _not_ the original vnode.</p></li>
<li><p>s[k:] is a string containing whatever follows the &#64;directive.
k has already been moved past any whitespace that follows the &#64;directive.</p></li>
</ul>
<p>See the add_directives plugins directive for a complete example of how to use
the ‘scan-directives’ hook.</p>
</li>
<li><p>g.app.closeLeoWindow calls the ‘close-frame’ hook just before
removing the window from g.app.windowList. The hook code may remove the window
from app.windowList to prevent g.app.closeLeoWindow from destroying the window.</p></li>
<li><p>New in Leo 6.4: Leo calls the ‘headkey1’ and ‘headkey2’ when the headline has <em>actually</em> changed.</p></li>
<li><p>p is the new node (position) containing <a class="reference external" href="mailto:'&#37;&#52;&#48;auto">‘<span>&#64;</span>auto</a> filename.ext’</p></li>
<li><p>The d argument to the open-with event handlers is a python
dictionary whose keys are all the tags specified by the user in the body of the
&#64;openwith node.</p></li>
</ol>
<p>The following events can <em>only</em> be called be called by minibuffer commands:</p>
<table class="docutils align-default">
<colgroup>
<col style="width: 47%" />
<col style="width: 15%" />
<col style="width: 38%" />
</colgroup>
<thead>
<tr class="row-odd"><th class="head"><p>Event name (tag argument)</p></th>
<th class="head"><p>Stop?</p></th>
<th class="head"><p>Keys in keywords dict</p></th>
</tr>
</thead>
<tbody>
<tr class="row-even"><td><p>‘iconclick1’</p></td>
<td><p>yes</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-odd"><td><p>‘iconrclick1’</p></td>
<td><p>yes</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-even"><td><p>‘iconrclick2’</p></td>
<td></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-odd"><td><p>‘icondclick1’</p></td>
<td><p>yes</p></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
<tr class="row-even"><td><p>‘icondclick2’</p></td>
<td></td>
<td><p>c,p,v,event (note 15)</p></td>
</tr>
</tbody>
</table>
<ol class="arabic" start="15">
<li><p>The only way to trigger these event is with the following minibuffer commands:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>        <span class="n">click</span><span class="o">-</span><span class="n">icon</span><span class="o">-</span><span class="n">box</span>
        <span class="n">ctrl</span><span class="o">-</span><span class="n">click</span><span class="o">-</span><span class="n">icon</span>
        <span class="n">double</span><span class="o">-</span><span class="n">click</span><span class="o">-</span><span class="n">headline</span>
<span class="n">Ctrl</span><span class="o">+</span><span class="n">F3</span> <span class="n">double</span><span class="o">-</span><span class="n">click</span><span class="o">-</span><span class="n">icon</span><span class="o">-</span><span class="n">box</span>
        <span class="n">right</span><span class="o">-</span><span class="n">click</span><span class="o">-</span><span class="n">headline</span>
        <span class="n">right</span><span class="o">-</span><span class="n">click</span><span class="o">-</span><span class="n">icon</span>
</pre></div>
</div>
</li>
</ol>
</section>
</section>
<section id="support-for-unit-testing">
<h2><a class="toc-backref" href="#id8">Support for unit testing</a><a class="headerlink" href="#support-for-unit-testing" title="Permalink to this heading">¶</a></h2>
<p>If a plugin has a function at the outer (module) level called unitTest, Leo’s unit tests will call that function.</p>
<p>So it would be good if writers of plugins would create such a unitTest function. To indicate a failure the unitTest can just throw an exception. Leo’s plugins test suite takes care of the rest.</p>
</section>
</section>


            <div class="clearer"></div>
          </div>
        </div>
      </div>
      <div class="sphinxsidebar" role="navigation" aria-label="main navigation">
        <div class="sphinxsidebarwrapper">
            <p class="logo"><a href="leo_toc.html">
              <img class="logo" src="_static/LeoLogo.svg" alt="Logo"/>
            </a></p>
  <div>
    <h4>Previous topic</h4>
    <p class="topless"><a href="coloring.html"
                          title="previous chapter">Controlling Syntax Coloring</a></p>
  </div>
  <div>
    <h4>Next topic</h4>
    <p class="topless"><a href="debuggers.html"
                          title="next chapter">Debugging with Leo</a></p>
  </div>
<div id="searchbox" style="display: none" role="search">
  <h3 id="searchlabel">Quick search</h3>
    <div class="searchformwrapper">
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
      <input type="submit" value="Go" />
    </form>
    </div>
</div>
<script>document.getElementById('searchbox').style.display = "block"</script>
        </div>
<div id="sidebarbutton" title="Collapse sidebar">
<span>«</span>
</div>

      </div>
      <div class="clearer"></div>
    </div>
    <div class="related" role="navigation" aria-label="related navigation">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="debuggers.html" title="Debugging with Leo"
             >next</a> |</li>
        <li class="right" >
          <a href="coloring.html" title="Controlling Syntax Coloring"
             >previous</a> |</li>
        <li class="nav-item nav-item-0"><a href="leo_toc.html">Leo 6.7.2 documentation</a> &#187;</li>
          <li class="nav-item nav-item-1"><a href="intermediatetopics.html" >Advanced Topics</a> &#187;</li>
        <li class="nav-item nav-item-this"><a href="">Writing Plugins</a></li> 
      </ul>
    </div>
    <div class="footer" role="contentinfo">
        &#169; Copyright 1997-2023, Edward K. Ream.
      Last updated on February 28, 2023.
      Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 5.3.0.
    </div>
  </body>
</html>